05:00
flexdashboard is the easiest way to get started making dashboards - at its core it is an RMarkdown document template that leverages CSS flexbox (+ a lot more) to generate attractive full page layouts that are well suited for publishing multiple data visualizations and related summaries and text.
Dashboards are constructed using a RMarkdown document
Row or column based layouts can be used
Structure is specified via markdown headings
Interactivity can be added by Shiny, but it is not required
Quarto is not currently supported
demos/demo02.Rmd
---
title: "Demo 02"
output:
flexdashboard::flex_dashboard:
orientation: rows
---
```{r global}
#| include: false
library(tidyverse)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
d_city = d |>
filter(city %in% "Chicago")
```
Row {data-height=650}
-------------------------------------
### Temperature
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=temp)) +
geom_line()
```
Row {data-height=350}
-------------------------------------
### Humidity
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=humidity)) +
geom_line()
```
###
```{r}
d_city |>
mutate(
day = lubridate::wday(time, label = TRUE, abbr = FALSE),
date = as.character(lubridate::date(time))
) |>
group_by(date, day) |>
summarize(
`min` = min(temp),
`max` = max(temp),
.groups = "drop"
) |>
knitr::kable()
```Open exercises/ex02.Rmd, which contains the code from the previous slide, and try knitting it.
Check that you are able to successfully render the flexdashboard.
If everything is working try modifying the code:
What happens if you remove orientation: rows from the front matter?
What happens if you change the Row text?
What happens if you change or remove {data-height=*}?
05:00
---
title: "Multipage layout"
output: flexdashboard::flex_dashboard
---
Page 1
=====================================
Column {data-width=600}
-------------------------------------
### Chart 1
```{r}
```
Column {data-width=400}
-------------------------------------
### Chart 2
```{r}
```
### Chart 3
```{r}
```
Page 2 {data-orientation=rows}
=====================================
Row {data-height=600}
-------------------------------------
### Chart 4
```{r}
```
Row {data-height=400}
-------------------------------------
### Chart 5
```{r}
```
### Chart 6
```{r}
```From the preceeding examples it is relatively straightforward to see how markdown is translated into the flexdashboard’s layout,
Level 1 headings (e.g. # or ==========) are used to designated Pages
Level 2 headings (e.g. ## or ----------) are used to designated columns or rows (depending on the orientation option)
Level 3 headings (e.g. ###) are used to designated chart / output elements
Horizontal rules (e.g. ***, ---, or ___ on a line by themselves) are used to separate the chart / output from the commentary with a storyboard layout
For the sharp-eyed among you, you may have noticed certain headings were given additional attributes via arguments wrapped in {}. These are CSS attributes that modify the display behavior of the elements they are attached to. Some common attribues,
data-height and data-width control the relative size of elements
data-padding or .no-padding control the padding around elements in pixels
data-orientation can be applied to pages to alter the orientation for a specific page
.tabset indicates a column or row should be composed on tabset elements
.sidebar indicates a sidebar should be included (local or global)
In the previous layout examples we saw the used some document options in the front matter, a couple of commonly used options that are worth knowning about:
orientation - default is columns, determines element layout orientation
vertical_layout - default is fill but scroll can be used to extend the viewable area
self_contained - default is TRUE, embeds all assessts within the html document (e.g. scripts, stylesheets, images, and videos)
theme - specifies a theme to use for styling (more on this later)
navbar - constructs a navigation bar at the top of the screen
We’ve just seen a number of possible layout methods for a flexdashboard, lets return to the code we’ve seen previously, provided in exercises/ex03.Rmd, and try changing layout more purposefully this time (e.g. a column, multipage, or storyboard layout).
Use this time to experiment with the different layout options and see what seems to work best.
Try out a multipage or storyboard layout
Try making a reasonable looking column layout
05:00
Because flexdashboards are just RMarkdown documents - we can leverage the parameterized reports functionality to pass in arguments (e.g. choice of city) while still keeping the final document static.
This is done by declaring the parameters in the front matter using the params field.
The values are then accessed via a read-only list called params within the report’s R chunks.
demos/demo03-1.Rmd
---
title: "Demo 03 - Part 1"
output:
flexdashboard::flex_dashboard:
orientation: rows
params:
city: "Los Angeles"
---
```{r setup}
#| include: false
library(tidyverse)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
d_city = d |>
filter(city %in% params$city)
```
Row {data-height=650}
-------------------------------------
### Temperature
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=temp)) +
ggtitle(params$city) +
geom_line()
```
Row {data-height=350}
-------------------------------------
### Humidity
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=humidity)) +
geom_line()
```
###
```{r}
d_city |>
mutate(
day = lubridate::wday(time, label = TRUE, abbr = FALSE),
date = as.character(lubridate::date(time))
) |>
group_by(date, day) |>
summarize(
`min` = min(temp),
`max` = max(temp),
.groups = "drop"
) |>
knitr::kable()
``` demos/demo03-2.Rmd
---
title: "Demo 03 - Part 2"
output:
flexdashboard::flex_dashboard:
orientation: rows
params:
city:
label: "City"
value: "Chicago"
input: select
choices: ["Chicago", "Durham", "Sedona", "New York",
"Los Angeles", "Seattle", "Omaha"]
---
```{r setup}
#| include: false
library(tidyverse)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
d_city = d |>
filter(city %in% params$city)
```
Row {data-height=650}
-------------------------------------
### Temperature
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=temp)) +
ggtitle(params$city) +
geom_line()
```
Row {data-height=350}
-------------------------------------
### Humidity
```{r}
#| echo: false
d_city |>
ggplot(aes(x=time, y=humidity)) +
geom_line()
```
###
```{r}
d_city |>
mutate(
day = lubridate::wday(time, label = TRUE, abbr = FALSE),
date = as.character(lubridate::date(time))
) |>
group_by(date, day) |>
summarize(
`min` = min(temp),
`max` = max(temp),
.groups = "drop"
) |>
knitr::kable()
```We can use Shiny components and reactivity in a flexdashboard (or any html output based RMarkdown document) by including runtime: shiny in the front matter.
This results in the document being served by shiny (which has implications for sharing and publishing)
demos/demo04.Rmd
---
title: "Demo 04"
output:
flexdashboard::flex_dashboard
runtime: shiny
---
```{r global}
library(tidyverse)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
```
```{r}
d_vars = d |>
select(where(is.numeric)) |>
names()
d_city = reactive({
d |>
filter(city %in% input$city)
})
```
Inputs {.sidebar}
-------------------------------------
```{r}
selectInput(
"city", "Select a city",
choices = c("Chicago", "Durham", "Sedona", "New York", "Los Angeles")
)
selectInput(
"var", "Select a variable",
choices = d_vars, selected = "humidity"
)
```
Col
-------------------------------------
### Temperature
```{r}
renderPlot({
d_city() |>
ggplot(aes(x=time, y=temp)) +
ggtitle(input$city) +
geom_line()
})
```
Col
-------------------------------------
### Other
```{r}
renderPlot({
d_city() |>
ggplot(aes(x=time, y=.data[[input$var]])) +
geom_line()
})
``` The shiny inputs do not need to live in a sidepanel - try rewriting Demo 04’s code such that the inputs for city and variable are located within the temperature and other elements respectively.
The base code is provided in exercises/ex04.Rmd, if you have extra time try playing around with the specific positioning of the input elements.
05:00
flexdashboard provides two built-in html components that can be included in your dashboard:
Value boxes:
Gauges:
Either component can be included in the dashboard with a static value via directly calling valueBox() or gauge()
Shiny reactive variants can be implemented using valueBoxOutput() with renderValueBox() or gaugeOutput() with renderGauge()
Both components take a color argument, this can be either
One of the standard bootstrap theme color names (i.e. “success”, “warning”, “danger”, “primary”, or “info”)
or any other valid CSS color specifier
Value box icons should use names from Font Awesome, Ionicons, or Bootstrap Glyphicons
demos/demo05.Rmd
---
title: "Demo 05 - value boxes and gauges"
output:
flexdashboard::flex_dashboard
runtime: shiny
---
```{r global}
library(tidyverse)
library(flexdashboard)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
```
```{r}
d_vars = d |>
select(where(is.numeric)) |>
names()
d_city = reactive({
d |>
filter(city %in% input$city)
})
```
Col {data-width=800}
-------------------------------------
###
```{r}
selectInput(
"city", "Select a city",
choices = c("Chicago", "Durham", "Sedona", "New York", "Los Angeles")
)
renderPlot({
d_city() |>
ggplot(aes(x=time, y=temp)) +
geom_line()
})
```
Col {data-width=200}
-------------------------------------
### Min temperature
```{r}
renderGauge({
gauge(
min(d_city()$temp),
min = 0, max=120, symbol = "°F",
gaugeSectors(success=c(60,90), warning=c(0,50), danger=c(90,120))
)
})
```
### Max temperature
```{r}
renderGauge({
gauge(
max(d_city()$temp),
min = 0, max=120, symbol = "°F",
gaugeSectors(success=c(60,90), warning=c(0,50), danger=c(90,120))
)
})
```
### Avg temperature
```{r}
renderValueBox({
avg = mean(d_city()$temp) |> round(1)
valueBox(
avg,
caption = "Avg temp",
icon = "fa-thermometer-half",
color = case_when(
avg >= 0 & avg < 50 ~ "warning",
avg >=50 & avg < 90 ~ "success",
avg >=90 & avg < 120 ~ "danger"
)
)
})
```This has nothing in particular to do with flexdashboard but is a super useful Shiny technique for improving interactivity.
Shiny’s plotOutput()s can also be used to generate inputs based on user click events. Here we are using the brush rectangular selection to subset the data and then updating the relevant components.
demos/demo06.Rmd
---
title: "Demo 06 - Linked brushing"
output:
flexdashboard::flex_dashboard
runtime: shiny
---
```{r global}
library(tidyverse)
library(flexdashboard)
ggplot2::theme_set(ggplot2::theme_bw())
d = readr::read_csv(here::here("data/weather.csv"))
```
```{r}
d_vars = d |>
select(where(is.numeric)) |>
names()
d_city = reactive({
d |>
filter(city %in% input$city)
})
d_selected = reactive({
db = shiny::brushedPoints(d_city(), input$plot_brush)
if (nrow(db) == 0)
db = d_city()
db
})
```
Col {data-width=800}
-------------------------------------
###
```{r}
selectInput(
"city", "Select a city",
choices = c("Chicago", "Durham", "Sedona", "New York", "Los Angeles")
)
renderPlot(
{
d_city() |>
ggplot(aes(x=time, y=temp)) +
geom_line()
},
outputArgs = list(
brush = shiny::brushOpts(id = "plot_brush")
)
)
```
Col {data-width=200}
-------------------------------------
### Starting time
```{r}
renderValueBox({
valueBox(
min(d_selected()$time),
caption = "Starting time",
icon = "fa-calendar-days"
)
})
```
### Ending time
```{r}
renderValueBox({
valueBox(
max(d_selected()$time),
caption = "Ending time",
icon = "fa-calendar-days"
)
})
```
### Min temperature
```{r}
renderGauge({
gauge(
min(d_selected()$temp),
min = 0, max=120, symbol = "°F",
gaugeSectors(success=c(60,90), warning=c(0,50), danger=c(90,120))
)
})
```
### Max temperature
```{r}
renderGauge({
gauge(
max(d_selected()$temp),
min = 0, max=120, symbol = "°F",
gaugeSectors(success=c(60,90), warning=c(0,50), danger=c(90,120))
)
})
```
### Avg temperature
```{r}
renderValueBox({
avg = mean(d_selected()$temp) |> round(1)
valueBox(
avg,
caption = "Avg temp",
icon = "fa-thermometer-half",
color = case_when(
avg >= 0 & avg < 50 ~ "warning",
avg >=50 & avg < 90 ~ "success",
avg >=90 & avg < 120 ~ "danger"
)
)
})
```posit::conf 2023 - Shiny Dashboards